package moltencore.game;

import spark.math.Vector2;

import java.util.LinkedList;

import moltencore.game.bubble.Bubble;
import moltencore.config.Config;

public class Player extends moltencore.player.Player implements ICannonLoadListener {
	public static enum State {
		Joined,
		ReadyToPlay,
		Playing
	}
	public class ActionMap {
		private boolean fire;
		private Cannon.Direction rotate;

		public void fire() {
			fire = true;
		}

		public void startRotating(Cannon.Direction direction) {
			//if we just start rotating reset the update timer to the update duration so the client will get
			//a notification immediately after the first update
			if(rotate == null) {
				elapsedSecondsSinceLastCannonRotationUpdate = cannonRotationUpdateFrequencySeconds;
			}
			this.rotate = direction;
		}

		public void stopRotating() {
			this.rotate = null;
			//since we stopped rotating tell the listeners the latest position of the target
			notifyListenersNewCannonTarget();
		}
	}

	private final float cannonRotationUpdateFrequencySeconds;
	private float elapsedSecondsSinceLastCannonRotationUpdate;

	private LinkedList<IGameEventListener> listeners = new LinkedList<IGameEventListener>();
	private ICannonFireListener cannonFireListener;
	private Cannon cannon;
	private Field field;
	private State state;
	private byte bubbleSetId;
	private ActionMap actionMap = new ActionMap();

	public Player(String name, byte bubbleSetId, long session, long clientId, CannonConfiguration cannonConfiguration) {
		super(name, session, clientId);
		this.bubbleSetId = bubbleSetId;
		cannonRotationUpdateFrequencySeconds = cannonConfiguration.getRotationUpdateFrequencySeconds();
		cannon = new Cannon(cannonConfiguration.getFireForce(), cannonConfiguration.getPanningSpeed());
		cannon.setCannonLoadListener(this);
		state = State.Joined;
	}

	public void update(float elapsedSeconds) {
		doCannonMovement(elapsedSeconds);
		doCannonFire();
	}

	private void doCannonFire() {
		if(!actionMap.fire) {
			return;
		}
		Bubble firedBubble = cannon.fire();
		if(cannonFireListener != null) {
			cannonFireListener.cannonFired(this);
		}
		field.spawnMovingBubble(firedBubble);
		actionMap.fire = false;
	}

	private void doCannonMovement(float elapsedSeconds) {
		if(actionMap.rotate == null) {
			return;
		}
		cannon.move(actionMap.rotate, elapsedSeconds);
		elapsedSecondsSinceLastCannonRotationUpdate += elapsedSeconds;
		if(elapsedSecondsSinceLastCannonRotationUpdate >= cannonRotationUpdateFrequencySeconds) {
			notifyListenersNewCannonTarget();
			elapsedSecondsSinceLastCannonRotationUpdate = 0;
		}
	}

	@Override
	public void cannonLoaded(Bubble bubble, int queuePosition) {
		if(queuePosition == 0) {
			notifyListenersBubbleNext(bubble);
		}
	}

	public Cannon getCannon() {
		return cannon;
	}

	public void setCannon(Cannon cannon) {
		this.cannon = cannon;
	}

	public Field getField() {
		return field;
	}

	public void setField(Field field) {
		this.field = field;
		field.addOwner(this);
		CannonData cannonData = field.getCannonData(getPositionIndex());
		System.out.println("getClientId() = " + getClientId());
		System.out.println("getPositionIndex() = " + getPositionIndex());
		cannon.getPosition().copy(cannonData.getPosition());
		cannon.getTarget().copy(cannonData.getTarget());
		for(IGameEventListener listener : listeners) {
			field.addGameEventListener(listener);
		}
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	public ActionMap getActionMap() {
		return actionMap;
	}

	public void addGameEventListener(IGameEventListener listener) {
		if(listeners.contains(listener)) {
			return;
		}
		listeners.add(listener);
		if(field != null) {
			field.addGameEventListener(listener);
		}
	}

	public void removeGameEventListener(IGameEventListener listener) {
		listeners.remove(listener);
		if(field != null) {
			field.removeGameEventListener(listener);
		}
	}

	public void setCannonFireListener(ICannonFireListener cannonFireListener) {
		this.cannonFireListener = cannonFireListener;
	}

	public byte getBubbleSetId() {
		return bubbleSetId;
	}

	private void notifyListenersNewCannonTarget() {
		Vector2 target = cannon.getTarget();
		for(IGameEventListener gameEventListener : listeners) {
			gameEventListener.newCannonTarget(getClientId(), target);
		}
	}

	private void notifyListenersBubbleNext(Bubble bubble) {
		for(IGameEventListener gameEventListener : listeners) {
			gameEventListener.bubbleNext(bubble, this.getField().getFieldId(), this.getClientId());
		}
	}
}
